iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
Software Development

一起看無間道學EdgeDB系列 第 19

[Day19] - 六幕:有內鬼終止交易

  • 分享至 

  • xImage
  •  

Full schema preview

本日所有schema搶先看

劇情提要

O記聯合CIB準備於今晚韓琛與泰國佬交易可卡因(古柯鹼)時,來個人贓並獲。建明知道後,假裝打電話給家人,通知韓琛。韓琛一直監聽警方頻道,並指示迪路和傻強四處亂晃,不要前往交易地點。過程中,永仁一直以摩斯密碼與黃sir聯絡。黃sir在得知韓琛監聽頻道後,隨即轉換頻道,並使用舊頻道發出今晚行動取消的指令。韓琛信以為真,指示迪路和傻強可以前往龍鼓灘交易。正當交易完成,黃sir準備先逮捕迪路和傻強將毒品扣下,再衝進屋逮捕韓琛之際,建仁使用簡訊傳送「有內鬼,終止交易」到韓琛所在位置附近的所有手機。

scene06

此劇照引用自IMDb-無間道

EdgeQL query

insert地點大廈三樓

insert Location {name:="大廈三樓"};

update wong

黃sir為有組織罪案及三合會調查科(O記)的警司(SP),update其相關property

update wong 
set {
    police_rank:= PoliceRank.SP,
    dept:= "有組織罪案及三合會調查科(O記)",
};

insert數名O記警察

insert黃sir麾下警察。

for i in range_unpack(range(1, 11))
union (
    insert Police {
        name:=  "police_" ++ <str>i,
        dept:= "有組織罪案及三合會調查科(O記)",
        police_rank:= PoliceRank.SPC,
    }
);

update lau

建明為刑事情報科(CIB)的高級督察(SIP),update其相關property

update lau 
set {
    police_rank:= PoliceRank.SIP,
    dept:= "刑事情報科(CIB)",
};

insert數名CIB警察

insert建明麾下警察。

insert Police{
    name:= "林國平",
    nickname:="大B",
    police_rank:= PoliceRank.SSGT,
    dept:= "刑事情報科(CIB)",
    actors:= (
        insert Actor{
            name:= "林家棟",
            eng_name:= "Gordon",
        }
    )
};

for name in {"大象", "孖八"}
union (
    insert Police {
        name:= name,
        nickname:= name,
        dept:= "刑事情報科(CIB)",
        police_rank:= PoliceRank.SGT,
    }
);

for i in range_unpack(range(11, 14))
union (
    insert Police {
        name:=  "police_" ++ <str>i,
        dept:= "刑事情報科(CIB)",
        police_rank:= PoliceRank.SPC,
    }
);

insert數名韓琛小弟

insert數名韓琛小弟,包括兩個小頭目,迪路與傻強。

insert Gangster {
    name:= "迪比亞路",
    nickname:= "迪路",
    gangster_boss:= hon,
    gangster_rank:= GangsterRank.Leader,
    actors:= (insert Actor {
        name:= "林迪安", 
        eng_name:="Dion",
    }),
};

insert Gangster {
    name:= "徐偉強",
    nickname:= "傻強",
    gangster_boss:= hon,
    gangster_rank:= GangsterRank.Leader,
    actors:= (insert Actor {
        name:= "杜汶澤", 
        eng_name:= "Edward",
    }),
};

for i in range_unpack(range(1, 11))
union (
    insert Gangster {
        name:=  "gangster_" ++ <str>i,
        gangster_boss:= hon,
        gangster_rank:= GangsterRank.Nobody,
    }
);

insert數個提及的地標

for loc in {"葵涌碼頭", "三號幹線", "龍鼓灘"}
union (
    insert Landmark{
        name:= loc,
    }
);

insert ChenLauContact

insert ChenLauContact {
    how:= "面對面",
    detail:= "黃sir帶隊進入韓琛毒品交易現場",
    `when`:= year_2002,
    where:= assert_single((select Location filter .name="大廈三樓")),
};

學習使用group - 情境1

讓我們假想一下自己是編劇。這個場景的人物相比前面多了不少,我們必須確認從警察的視角來看,各個階級的人力配置合不合理,此時group會是一個不錯的工具。進行group之後的操作,大多需要用到它的keygroupingelements

首先我們做以下嘗試:

  • with區塊選取PolicePoliceSpyGangsterSpy(建明及永仁也都是警察)命名為p
  • with區塊group p並依照police_rank來分類,結果命名為g
  • 最後使用{**}顯示g的細節(註1)。
with p:= Police union PoliceSpy union GangsterSpy,
     g:= (group p by .police_rank),
select g {**};

但這麼一來,結果會非常長,我們舉黃sir為例,因為SP級別只有他一人。

...
{
id: 10d5423b-15cf-4c4d-89ca-e39b4bfa9902,
grouping: {'police_rank'},
key: {id: 46542b84-47fb-44b8-bdfd-2bc34e924eee, police_rank: SP},
elements: {
  default::Police {
    is_officer: true,
    classic_lines: ['你25號生日嘛!25仔!'],
    eng_name: {},
    name: '黃志誠',
    nickname: '黃sir',
    dept: '有組織罪案及三合會調查科(O記)',
    police_rank: SP,
    id: db630896-bc48-11ee-aae4-df71814b08b4,
  },
},
},
...

之所以會得到這麼長的結果是因為使用了{**},但是我們的目的僅是想知道各個階級的人數,所以可以做下列修改:

  • 利用groupkey可以得到police_rank(因為這個property就是我們在by時使用的),並在g{ }中命名為police_rank
  • 利用groupelements得到該分類中每一個object(即PolicePoliceSpyGangsterSpy),接著使用count來計算其數量,並在g{ }中命名為counts
  • 最後,由於police_rank是一個enum,所以可以使用order by對其進行排序,並加上desc,這麼一來就會改成官階較大的排在前面。
with p:= Police union PoliceSpy union GangsterSpy,
     g:= (group p by .police_rank),
select g {police_rank:= .key.police_rank, 
          counts:= count(.elements)}
order by .police_rank desc;
{
  {police_rank: SP, counts: 1},
  {police_rank: SIP, counts: 1},
  {police_rank: SSGT, counts: 1},
  {police_rank: SGT, counts: 2},
  {police_rank: SPC, counts: 13},
  {police_rank: Protected, counts: 1},
}

這麼一來就可以看出:

  • 高階長官SPSIP各一位
  • 比較有工作經驗的SSGT一位及SGT兩位。
  • 主要執勤探員SPC十三位。
  • 臥底探員一位。

編劇此時可以依據這個結果,來請教相關專業人士這樣的人力配置是否合理。

學習使用group - 情境2

假設片場工作人員在休息時間聊到,不知道劇中出現的地名:

  • 最長的有多長呢?
  • 哪一個長度又是出現最多次的呢?

此時又是可以好好運用group的機會囉。因為這次不像上面一樣,有內建的police_rank可以使用,所以需要使用using來組合出想要如何分類的操作。

針對第一個問題:

  • 首先對name property使用len,並取名為name_length,這樣就可以將其放在by之後來分類。
  • 利用groupkey得到剛剛定義的name_length,並在g{ }中命名為name_length
  • 利用groupelements得到該分類中每一個Place,接著使用count來計算其數量,並在g{ }中命名為counts
  • 利用groupelements得到name property,並在g{ }中命名為names
  • 最後使用order byname_length進行排序,並加上desc,這麼一來長度較長的地名就會顯示在前面了。
with g:= (group Place 
          using name_length:= len(.name)
          by name_length),
select g {name_length:= .key.name_length ,
          counts:= count(.elements),
          names:= .elements.name}
order by .name_length desc;
{
  {name_length: 6, counts: 1, names: {'Hi-Fi鋪'}},
  {name_length: 4, counts: 3, names: {'大廈三樓', '葵涌碼頭', '三號幹線'}},
  {name_length: 3, counts: 2, names: {'警察局', '龍鼓灘'}},
  {name_length: 2, counts: 3, names: {'佛堂', '天台', '警校'}},
}

結果發現長度最長的地名是Hi-Fi鋪,長度為6。

針對第二個問題,只需將order by的目標改為counts即可。

with g:= (group Place 
          using name_length:= len(.name)
          by name_length),
select g {name_length:= .key.name_length ,
          counts:= count(.elements),
          names:= .elements.name}
order by .counts desc;
{
  {name_length: 4, counts: 3, names: {'大廈三樓', '葵涌碼頭', '三號幹線'}},
  {name_length: 2, counts: 3, names: {'佛堂', '天台', '警校'}},
  {name_length: 3, counts: 2, names: {'警察局', '龍鼓灘'}},
  {name_length: 6, counts: 1, names: {'Hi-Fi鋪'}},
}

結果發現地名長度為4及2的組別都出現三次。

insert此場景的Scene

這裡選擇人物時,其實也可以像前面一樣使用PoliceGangster。但這麼一來,就是假設要選取全部的PoliceGangster,如果之後我們修改了前面幾個場景的query,在現在這種人物比較多的場景會難以偵錯(您可以假想Scene被極度簡化,只包含重要演員,但配角及台前幕後許多工作人員都未計入)。

所以我們這邊示範使用with搭配filter,限縮選取範圍。

with policemen:= (
                    select Police 
                    filter .dept in {"有組織罪案及三合會調查科(O記)", 
                                     "刑事情報科(CIB)"}
                 ),
     gangsters:= (select Gangster filter .gangster_boss=hon)
insert Scene {
      title:= "有內鬼終止交易",
      detail:= "O記聯合CIB準備於今晚韓琛與泰國佬交易可卡因(古柯鹼)時,來個" ++
               "人贓並獲。建明知道後,假裝打電話給家人,通知韓琛。韓琛一直監" ++
               "聽警方頻道,並指示迪路和傻強四處亂晃,不要前往交易地點。過程中," ++
               "永仁一直以摩斯密碼與黃sir聯絡。黃sir在得知韓琛監聽頻道後,隨即" ++
               "轉換頻道,並使用舊頻道發出今晚行動取消的指令。韓琛信以為真,指示" ++
               "迪路和傻強可以前往龍鼓灘交易。正當交易完成,黃sir準備先逮捕迪路" ++
               "和傻強將毒品扣下,再衝進屋逮捕韓琛之際,建仁使用簡訊傳送「有內鬼" ++
               ",終止交易」到韓琛所在位置附近的所有手機。",
      who:= policemen union gangsters union {chen, lau, hon},
      `when`:= year_2002,
      where:= (select Place filter .name="大廈三樓" or .name="龍鼓灘"),         
      remarks:= "1.假設國平官階為`SSGT`,大象與孖八官階為`SGT`。"  
};

備註

註1:或許您會很想只顯示key裡面的police_rankelements裡面的name,卻發現不知道怎麼達成。您可能會做以下嘗試:

#❌
with p:= Police union PoliceSpy union GangsterSpy,
     g:= (group p by .police_rank),
select g {key {police_rank}, 
          elements {name}};

但正確的語法是需要加上:

#✅
with p:= Police union PoliceSpy union GangsterSpy,
     g:= (group p by .police_rank),
select g {key: {police_rank}, 
          elements: {name}};
{
  {
    key: {police_rank: Protected}, 
    elements: {default::PoliceSpy {name: '陳永仁'}}},
  {
    key: {police_rank: SPC},
    elements: {
      default::Police {name: 'police_1'},
      default::Police {name: 'police_2'},
      default::Police {name: 'police_3'},
      default::Police {name: 'police_4'},
      default::Police {name: 'police_5'},
      default::Police {name: 'police_6'},
      default::Police {name: 'police_7'},
      default::Police {name: 'police_8'},
      default::Police {name: 'police_9'},
      default::Police {name: 'police_10'},
      default::Police {name: 'police_11'},
      default::Police {name: 'police_12'},
      default::Police {name: 'police_13'},
    },
  },
  {
    key: {police_rank: SGT}, 
    elements: {default::Police {name: '大象'}, default::Police {name: '孖八'}}
  },
  {key: {police_rank: SSGT}, elements: {default::Police {name: '林國平'}}},
  {key: {police_rank: SIP}, elements: {default::GangsterSpy {name: '劉建明'}}},
  {key: {police_rank: SP}, elements: {default::Police {name: '黃志誠'}}},
}

這也是一個我們經常會突然失憶的地方,分享給大家作為參考。

無間假設

  • 建明與黃sir的職級稍後才會提及,我們提前於此與部門一起update
  • 建明的小組成員其實於前一幕假扮律師時已經出現,我們省略該場景,改於此處 insert
  • 假設大象與孖八的兩人的本名也是大象與孖八。兩人應該是隸屬於O記黃sir手下,但因為CIB建明手下於劇中大多沒有出現人名,所以假設兩人為CIB部門。
  • 三號幹線是劇中稍後才會出現的地名,提前移至此處insert

無間吹水

  • 建明傳送的簡訊原文是「有內鬼,終止交易」,但最終於路人手機上顯示的是「有內鬼終止交易」,少了一個逗號。
  • 本片開頭韓琛提到以前在屯門做代客泊車的工作,可以得知其為屯門當地人或是與該地淵緣深厚,所以毒品交易地點選擇附近的龍鼓灘也甚為合理。
  • 本場景有O記及CIB部門,卻沒有毒品調查科(MB)參與,有點令人費解。

參考資料

無間EdgeDB六幕:有內鬼終止交易


上一篇
[Day18] - 五幕:三年之後又三年
下一篇
[Day20] - 七幕:互猜底牌
系列文
一起看無間道學EdgeDB30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言